//---------------------------------------------------------------------
//  This file is part of the Windows Workflow Foundation SDK Code Samples.
// 
//  Copyright (C) Microsoft Corporation.  All rights reserved.
// 
//This source code is intended only as a supplement to Microsoft
//Development Tools and/or on-line documentation.  See these other
//materials for detailed information regarding Microsoft code samples.
// 
//THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
//KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//PARTICULAR PURPOSE.
//---------------------------------------------------------------------
using System;
using System.IO;
using System.Xml;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Serialization;
using System.Collections;
using System.Collections.Generic;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Workflow.Activities.Rules;
using System.Workflow.Activities.Rules.Design;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Text;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Net;


namespace Crab.WorkflowClient
{
    #region WorkflowLoader
    internal sealed class WorkflowLoader : WorkflowDesignerLoader
    {
        private string xomlFile = string.Empty;
        private StringBuilder tempRulesStream = null;
        byte[] byteArr = new byte[50000];
        private Stream streamOut;
        private string supportAssemblyPath = string.Empty;
        private CodeCompileUnit codeBesideccu;
        private CodeCompileUnit xamlccu;
        private Activity rootActivity = null;


        #region ctor
        internal WorkflowLoader()
        {
            // Create the code compile unit 

            codeBesideccu = new CodeCompileUnit();
            codeBesideccu.ReferencedAssemblies.Add("System.Workflow.Activities");
            CodeNamespace ns = new CodeNamespace("foo");
            ns.Imports.Add(new CodeNamespaceImport("System"));
            ns.Imports.Add(new CodeNamespaceImport("System.ComponentModel"));
            ns.Imports.Add(new CodeNamespaceImport("System.ComponentModel.Design"));
            ns.Imports.Add(new CodeNamespaceImport("System.Workflow.ComponentModel.Design"));
            ns.Imports.Add(new CodeNamespaceImport("System.Workflow.ComponentModel"));
            ns.Imports.Add(new CodeNamespaceImport("System.Workflow.ComponentModel.Serialization"));
            ns.Imports.Add(new CodeNamespaceImport("System.Workflow.ComponentModel.Compiler"));
            ns.Imports.Add(new CodeNamespaceImport("System.Drawing"));
            ns.Imports.Add(new CodeNamespaceImport("System.Collections"));
            ns.Imports.Add(new CodeNamespaceImport("System.Workflow.Activities"));
            ns.Imports.Add(new CodeNamespaceImport("System.Workflow.ComponentModel"));
            ns.Imports.Add(new CodeNamespaceImport("System.Workflow.Runtime"));
            codeBesideccu.Namespaces.Add(ns);
            CodeTypeDeclaration ctd = new CodeTypeDeclaration("Workflow1");
            ctd.BaseTypes.Add("System.Workflow.Activities.SequentialWorkflowActivity");
            ctd.IsPartial = true;
            ctd.Attributes = MemberAttributes.Public;
            ns.Types.Add(ctd);
        }

        #endregion

        protected override void Initialize()
        {
            base.Initialize();

            // Add all the services to the loaderhost
            IDesignerLoaderHost host = LoaderHost;
            if (host != null)
            {
                TypeProvider typeProvider = new TypeProvider(host);
                typeProvider.AddCodeCompileUnit(this.CodeBesideCCU);
                typeProvider.AddAssemblyReference(typeof(System.EventHandler).Assembly.Location);
                typeProvider.AddAssemblyReference(typeof(System.ComponentModel.AttributeCollection).Assembly.Location);
                typeProvider.AddAssemblyReference(typeof(System.Workflow.ComponentModel.CompositeActivity).Assembly.Location);
                typeProvider.AddAssemblyReference(typeof(System.Workflow.Activities.SequentialWorkflowActivity).Assembly.Location);

                host.AddService(typeof(ITypeProvider), typeProvider, true);
            }
        }

        public override void Dispose()
        {
            IDesignerLoaderHost host = LoaderHost;
            if (host != null)
            {
                host.RemoveService(typeof(IMenuCommandService));
            }

            base.Dispose();
        }

        public override string FileName
        {
            get
            {
                return this.xomlFile;
            }
        }

        internal string XomlFile
        {
            get
            {
                return this.xomlFile;
            }
            set
            {
                this.xomlFile = value;
            }
        }

	
        internal Stream StreamOut
        {
            get
            {
                return this.streamOut;
            }
            set
            {
                this.streamOut = value;
            }
        }

        public string SupportAssemblyPath
        {
            get
            {
                return this.supportAssemblyPath;
            }

            set
            {
                this.supportAssemblyPath = value;
            }
        }
        public Activity RootActivity
        {
            get
            {
                return this.rootActivity;
            }

            set
            {
                this.rootActivity = value;
            }
        }
        public CodeCompileUnit CodeBesideCCU
        {
            get { return codeBesideccu; }
            set { codeBesideccu = value; }
        }

        public CodeCompileUnit XamlCodeCompileUnit
        {
            get { return xamlccu; }
            set { xamlccu = value; }
        }
        public override TextReader GetFileReader(string filePath)
        {
            if (this.tempRulesStream != null)
                return new StringReader(this.tempRulesStream.ToString());
            else
                return null;
        }

        public override TextWriter GetFileWriter(string filePath)
        {
            this.tempRulesStream = new StringBuilder();
            return new StringWriter(this.tempRulesStream);
        }


        protected override void PerformLoad(IDesignerSerializationManager serializationManager)
        {
            base.PerformLoad(serializationManager);
            if (!this.xomlFile.Equals(string.Empty))
            {
                IDesignerHost designerHost = (IDesignerHost)GetService(typeof(IDesignerHost));

                // get the root activity and add the corresponding object graph to the designer host
                rootActivity = DeserializeXoml();

                if (rootActivity != null && designerHost != null)
                {
                    AddObjectGraphToDesignerHost(designerHost, rootActivity);
                    Type companionType = rootActivity.GetValue(WorkflowMarkupSerializer.XClassProperty) as Type;
                    if (companionType != null)
                        SetBaseComponentClassName(companionType.FullName);
                }

                designerHost.Activate();

                /*
                // Read from rules file if one exists
                string destinationPath = string.Format("{0}.rules", this.tenantId);

                if (!File.Exists(destinationPath))
                {
                    // download rules from the webserver
                    WorkflowService wfserver = new WorkflowService();

                    string content = wfserver.GetRulesByTenantID(this.TenantId,this.UserId);
                    //string rulesRemoteFile = Regex.Replace(url, ".xoml", ".rules");
                    WorkflowService.WriteFile(content, destinationPath);
                }

                // successfuly downloaded
                if (File.Exists(destinationPath))
                {
                    StreamReader rulesReader = new StreamReader(destinationPath);
                    this.tempRulesStream = new StringBuilder(rulesReader.ReadToEnd());
                    rulesReader.Close();
                }
                */
            }
        }

        private Activity DeserializeXoml()
        {
            Activity rootActivity = null;
            if ((this.supportAssemblyPath == String.Empty) || (this.supportAssemblyPath == null))
            {
                XmlReader reader = new XmlTextReader(this.xomlFile);
                try
                {
                    WorkflowMarkupSerializer xomlSerializer = new WorkflowMarkupSerializer();
                    rootActivity = xomlSerializer.Deserialize(reader) as Activity;
                }
                finally
                {
                    reader.Close();
                }
            }
            else
            {
                ServiceContainer serviceContainer = new ServiceContainer();
                DesignerSerializationManager serializationManager = new DesignerSerializationManager(serviceContainer);
                WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();
                TypeProvider typeProvider = new TypeProvider((IServiceProvider)serializationManager);
                typeProvider.AddAssembly(Assembly.LoadFrom(this.supportAssemblyPath));
                serviceContainer.AddService(typeof(ITypeProvider), typeProvider);

                using (serializationManager.CreateSession())
                {
                    try
                    {
                        using (XmlReader reader = XmlReader.Create(this.xomlFile))
                        {
                            rootActivity = (Activity)serializer.Deserialize((IDesignerSerializationManager)serializationManager, reader);
                            foreach (object error in serializationManager.Errors)
                                Console.WriteLine("ERROR: {0}", error);
                        }
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("Exception: {0}", e);
                    }
                }
            }
            return rootActivity;
        }

        protected override void PerformFlush(IDesignerSerializationManager manager)
        {
            IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost));
            Activity rootActivity = host.RootComponent as Activity;

            //refresh the rules stream
            FlushRules(rootActivity);  //nick

            if (host != null && host.RootComponent != null)
            {

                if (rootActivity != null)
                {
                    XmlTextWriter xmlWriter = new XmlTextWriter(this.xomlFile, Encoding.UTF8);
                    try
                    {
                        WorkflowMarkupSerializer xomlSerializer = new WorkflowMarkupSerializer();
                        xomlSerializer.Serialize(xmlWriter, rootActivity);
                    }
                    finally
                    {
                        xmlWriter.Close();
                    }
                }
            }

            if (this.tempRulesStream != null)
            {
                string rulesFile = Path.Combine(Path.GetDirectoryName(this.xomlFile), Path.GetFileNameWithoutExtension(this.xomlFile) + ".rules");
                using (StreamWriter rulesWriter = new StreamWriter(rulesFile))
                {
                    rulesWriter.Write(this.tempRulesStream.ToString());
                }
            }
        }

        public override void Flush()
        {
            PerformFlush(null);
        }

        /// <summary>
        /// refresh the rules stream
        /// </summary>
        /// <param name="activity"></param>
        private void FlushRules(Activity activity)
        {
            RuleDefinitions definitions1 = (RuleDefinitions)activity.GetValue(RuleDefinitions.RuleDefinitionsProperty);
            if (definitions1 != null)
            {
                WorkflowDesignerLoader loader1 = this;
                string text1 = string.Empty;
                if (!string.IsNullOrEmpty(loader1.FileName))
                {
                    text1 = Path.Combine(Path.GetDirectoryName(loader1.FileName), Path.GetFileNameWithoutExtension(loader1.FileName));
                }
                text1 = text1 + ".rules";
                using (TextWriter writer1 = loader1.GetFileWriter(text1))
                {
                    if (writer1 != null)
                    {
                        using (XmlWriter writer2 = CreateXmlWriter(writer1))
                        {
                            new WorkflowMarkupSerializer().Serialize(writer2, definitions1);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Create XmlWriter from TextWriter
        /// </summary>
        /// <param name="output"></param>
        /// <returns></returns>
        private static XmlWriter CreateXmlWriter(object output)
        {
            XmlWriterSettings settings1 = new XmlWriterSettings();
            settings1.Indent = true;
            settings1.IndentChars = "\t";
            settings1.OmitXmlDeclaration = true;
            settings1.CloseOutput = true;
            if (output is string)
            {
                return XmlWriter.Create(output as string, settings1);
            }
            if (output is TextWriter)
            {
                return XmlWriter.Create(output as TextWriter, settings1);
            }
            return null;
        }

        public Stream FlushStream()
        {
            streamOut = new MemoryStream(byteArr, true);
            IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost));
            Activity rootActivity = host.RootComponent as Activity;
            if (host != null && host.RootComponent != null)
            {

                if (rootActivity != null)
                {
                    XmlTextWriter xmlWriter = new XmlTextWriter(this.streamOut, Encoding.Default);
                    try
                    {
                        WorkflowMarkupSerializer xomlSerializer = new WorkflowMarkupSerializer();
                        xomlSerializer.Serialize(xmlWriter, rootActivity);
                    }
                    finally
                    {
                        xmlWriter.Close();
                    }
                }
            }
            return this.streamOut;
        }

        private static void AddObjectGraphToDesignerHost(IDesignerHost designerHost, Activity activity)
        {
            Guid Definitions_Class = new Guid("3FA84B23-B15B-4161-8EB8-37A54EFEEFC7");

            if (designerHost == null)
                throw new ArgumentNullException("designerHost");
            if (activity == null)
                throw new ArgumentNullException("activity");

            string rootSiteName = activity.QualifiedName;
            if (activity.Parent == null)
            {
                string fullClassName = activity.UserData[Definitions_Class] as string;
                if (fullClassName == null)
                    fullClassName = activity.GetType().FullName;
                rootSiteName = (fullClassName.LastIndexOf('.') != -1) ? fullClassName.Substring(fullClassName.LastIndexOf('.') + 1) : fullClassName;
                designerHost.Container.Add(activity, rootSiteName);
            }
            else
            {
                designerHost.Container.Add(activity, activity.QualifiedName);
            }

            if (activity is CompositeActivity)
            {
                foreach (Activity activity2 in GetNestedActivities(activity as CompositeActivity))
                    designerHost.Container.Add(activity2, activity2.QualifiedName);
            }
        }

        private static Activity[] GetNestedActivities(CompositeActivity compositeActivity)
        {
            if (compositeActivity == null)
                throw new ArgumentNullException("compositeActivity");

            IList<Activity> childActivities = null;
            ArrayList nestedActivities = new ArrayList();
            Queue compositeActivities = new Queue();
            compositeActivities.Enqueue(compositeActivity);
            while (compositeActivities.Count > 0)
            {
                CompositeActivity compositeActivity2 = (CompositeActivity)compositeActivities.Dequeue();
                childActivities = compositeActivity2.Activities;

                foreach (Activity activity in childActivities)
                {
                    nestedActivities.Add(activity);
                    if (activity is CompositeActivity)
                        compositeActivities.Enqueue(activity);
                }
            }
            return (Activity[])nestedActivities.ToArray(typeof(Activity));
        }

        internal static void DestroyObjectGraphFromDesignerHost(IDesignerHost designerHost, Activity activity)
        {
            if (designerHost == null)
                throw new ArgumentNullException("designerHost");
            if (activity == null)
                throw new ArgumentNullException("activity");

            designerHost.DestroyComponent(activity);

            if (activity is CompositeActivity)
            {
                foreach (Activity activity2 in GetNestedActivities(activity as CompositeActivity))
                    designerHost.DestroyComponent(activity2);
            }
        }

    }
    #endregion


}
